Parser.php

<?php

namespace Breg;

class Parser {


    // protected $regex;


    protected $patterns = [
        'function'=> '/\:\:([a-zA-Z0-9\_]*)\((.*)\)/',
        'comment'=>'/(?:^|\s)#[^\n]*$/m',
        'pad'=> '/(^\s*|\s*$)/m',
        'esc_trail_slash'=>'/(^|[^\\\\])((?:\\\\\\\\)*)(\\\\)$/m',
        'replace_esc_trail_slash'=>'$1$2\\ ',
    ];



    /**
     * parse a regex string into an array
     * 
     */
    public function parse($regex){
        $parsed = ['source_reg'=>$regex, 'functions'=>[], 'clean_reg'=>null];
        $reg = $regex;
        $cleanReg = $reg;
        $cleanReg = $this->removeComments($cleanReg);
        $cleanReg = $this->removePad($cleanReg);
        $cleanReg = $this->addEscapedTrailingSpace($cleanReg);
        $cleanReg = $this->implodeLines($cleanReg);
        $parsed['clean_reg'] = $cleanReg;

        $split = explode("\n",$reg);
        $lines = array_map([$this,'cleanLine'], $split);
        
        $functions = [];
        $i = 0;
        do {
            $line = $lines[$i];
            $func = $this->parseRegFunc($line);
            if ($func==false)continue;

            $parsed['functions'][] = $func;
        } while (count($lines)>++$i);

            // echo "\n\n";
            // var_dump($parsed);
            // echo "\n\n";
            // exit;
        return $parsed;
    }

    // public function replace($function, $string){
    //     $this->regex = str_replace($function->declaration, $string,$this->regex);
    // }
    //

    protected function removeComments($reg){
        $reg = preg_replace($this->patterns['comment'],'',$reg);
        return $reg;
    }
    protected function removePad($reg){
        $reg = preg_replace($this->patterns['pad'],'',$reg);
        return $reg;
    }
    protected function addEscapedTrailingSpace($reg){
        $reg = preg_replace($this->patterns['esc_trail_slash'], $this->patterns['replace_esc_trail_slash'], $reg);
        return $reg;
    }
    protected function implodeLines($reg){
        $arrayOfLines = explode("\n",
                    str_replace(["\r\n","\n\r","\r"],"\n",$reg)
            );
        $reg = implode('',$arrayOfLines);
        return $reg;
    }



    /**
     * Each comment must start with ` #` (space hash). Everything following in that line is comment and will be removed from output
     * If your pattern uses ` #`, escape it like ` \#`
     * 
     * @export(Syntax.Comments)
     */
    protected function cleanLine($regLine){
        // $
        $pos = strpos($regLine,' ## ');
        if ($pos!==false)$regLine = substr($regLine,0,$pos);
        $regLine = trim($regLine);
        return trim($regLine);
    }

    /**
     * ```
     * ::functionName(arg1 ;; arg2 ;; arg3) ## Comments if you want
     * ```
     * - The function must be the only thing on the line. Comments allowed.  
     * - Args must **not** be quoted. Every arg will be trimmed (surrounding whitespace is removed).  
     * - Args are separated by ' ;; ' (space semicolon semicolon space)
     * 
     * 
     * @export(Syntax.Functions)
     */
    protected function parseRegFunc($line){

        // $funcReg = '/^\:\:([a-zA-Z0-9]*)\((.*)\)$/';
        $funcReg = '/\:\:([a-zA-Z0-9]*)\((.*)\)/';
        // $refListReg = '/\{\{([a-zA-Z\.0-9]+)\}\}/';
        $matches = [];
        $match = preg_match($this->patterns['function'],$line,$matches);
        if (!$match) return false;
        $specialCharsReg = '\.\-\_';
        /**
         * Functions can accept a refs list as an argument. Each ref is closed in double moustaches `{{refname}}`
         * ```
         * ## Combine selected refs with the bar (|) separator
         * ::combine( {{ref1}}{{ref2}}{{ref3}} ;; |)
         * ```
         * Each ref name can be composed of `a-z`, `A-Z`, `0-9`, and/or special chars: `. - _`
         * 
         * @export(Syntax.Refs)
         */
        $getRefs = '/\{\{([a-zA-Z'.$specialCharsReg.'0-9]+)\}\}/';
        $isRefsArg = '/^(\{\{([a-zA-Z'.$specialCharsReg.'0-9]+)\}\})+$/';
        
        $funcName = $matches[1];
        $argsList = explode(' ;; ',$matches[2]);
        $finalArgList = [];

        $func = ['definition'=>$matches[0],'name'=>$funcName,'args'=>[], 'argString'=>$matches[2]];
        while ($arg = current($argsList)){
            next($argsList);
            $arg = trim($arg);
            if (preg_match($isRefsArg,$arg)){
                // echo "IS REF ARG: ".$arg."\n\n";
                $refs = [];
                preg_match_all($getRefs,$arg,$refs);
                // $func['items'][] = $refs[1];
                // var_dump($refs);
                // $arg = $this->namespace.'.'.$refs[1][0];
                $arg = $refs[1];
            }
            $func['args'][] = $arg;
        }

        return $func;
    }
}